home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
130 MIDI Tool Box
/
130 MIDI Tool Box.iso
/
kbmidi
/
kbmidi.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-01-27
|
29KB
|
777 lines
/*---------------------------------------------------------
KBMIDI.C -- Keyboard MIDI Player for Multimedia Windows
(c) Charles Petzold, 1992
---------------------------------------------------------*/
#include <windows.h>
extern "C" {
#include <mmsystem.h>
}
#include <string.h>
typedef unsigned int UINT ;
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
// Defines for Menu IDs
// --------------------
#define IDM_OPEN 0x100
#define IDM_CLOSE 0x101
#define IDM_DEVICE 0x200
#define IDM_CHANNEL 0x300
#define IDM_VOICE 0x400
// Forward declaration of WndProc
// ------------------------------
long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG);
// Global variables
// ----------------
char szAppName [] = "KBMidi" ;
HMIDIOUT hMidiOut ;
int iDevice = MIDIMAPPER, iChannel = 0, iVoice = 0, iVelocity = 64 ;
int cxCaps, cyChar, xOffset, yOffset ;
// Structures for family and instrument names
// ------------------------------------------
typedef struct
{
char * szInst ;
int iVoice ;
}
INSTRUMENT ;
typedef struct
{
char * szFam ;
INSTRUMENT inst [8] ;
}
FAMILY ;
FAMILY fam [16] = { "Piano",
"Acoustic Grand Piano", 0,
"Bright Acoustic Piano", 1,
"Electric Grand Piano", 2,
"Honky-tonk Piano", 3,
"Rhodes Piano", 4,
"Chorused Piano", 5,
"Harpsichord", 6,
"Clavinet", 7,
"Chromatic Percussion",
"Celesta", 8,
"Glockenspiel", 9,
"Music box", 10,
"Vibraphone", 11,
"Marimba", 12,
"Xylophone", 13,
"Tubular Bells", 14,
"Dulcimer", 15,
"Organ",
"Hammond Organ", 16,
"Percussive Organ", 17,
"Rock Organ", 18,
"Church Organ", 19,
"Reed Organ", 20,
"Accordian", 21,
"Harmonica", 22,
"Tango Accordian", 23,
"Guitar",
"Acoustic Guitar (nylon)", 24,
"Acoustic Guitar (steel)", 25,
"Electric Guitar (jazz)", 26,
"Electric Guitar (clean)", 27,
"Electric Guitar (muted)", 28,
"Overdriven Guitar", 29,
"Distortion Guitar", 30,
"Guitar Harmonics", 31,
"Bass",
"Acoustic Bass", 32,
"Electric Bass (finger)", 33,
"Electric Bass (pick)", 34,
"Fretless Bass", 35,
"Slap Bass 1", 36,
"Slap Bass 2", 37,
"Synth Bass 1", 38,
"Synth Bass 2", 39,
"Strings",
"Violin", 40,
"Viola", 41,
"Cello", 42,
"Contrabass", 43,
"Tremolo Strings", 44,
"Pizzicato Strings", 45,
"Orchestral Harp", 46,
"Timpani", 47,
"Ensemble",
"String Ensemble 1", 48,
"String Ensemble 2", 49,
"Synth Strings 1", 50,
"Synth Strings 2", 51,
"Choir Aahs", 52,
"Voice Oohs", 53,
"Synth Voice", 54,
"Orchestra Hit", 55,
"Brass",
"Trumpet", 56,
"Trombone", 57,
"Tuba", 58,
"Muted Trumpet", 59,
"French Horn", 60,
"Brass Section", 61,
"Synth Brass 1", 62,
"Synth Brass 2", 63,
"Reed",
"Soprano Sax", 64,
"Alto Sax", 65,
"Tenor Sax", 66,
"Baritone Sax", 67,
"Oboe", 68,
"English Horn", 69,
"Bassoon", 70,
"Clarinet", 71,
"Pipe",
"Piccolo", 72,
"Flute", 73,
"Recorder", 74,
"Pan Flute", 75,
"Bottle Blow", 76,
"Shakuhachi", 77,
"Whistle", 78,
"Ocarina", 79,
"Synth Lead",
"Lead 1 (square)", 80,
"Lead 2 (sawtooth)", 81,
"Lead 3 (caliope lead)", 82,
"Lead 4 (chiff lead)", 83,
"Lead 5 (charang)", 84,
"Lead 6 (voice)", 85,
"Lead 7 (fifths)", 86,
"Lead 8 (brass + lead)", 87,
"Synth Pad",
"Pad 1 (new age)", 88,
"Pad 2 (warm)", 89,
"Pad 3 (polysynth)", 90,
"Pad 4 (choir)", 91,
"Pad 5 (bowed)", 92,
"Pad 6 (metallic)", 93,
"Pad 7 (halo)", 94,
"Pad 8 (sweep)", 95,
"Synth Effects",
"FX 1 (rain)", 96,
"FX 2 (soundtrack)", 97,
"FX 3 (crystal)", 98,
"FX 4 (atmosphere)", 99,
"FX 5 (brightness)", 100,
"FX 6 (goblins)", 101,
"FX 7 (echoes)", 102,
"FX 8 (sci-fi)", 103,
"Ethnic",
"Sitar", 104,
"Banjo", 105,
"Shamisen", 106,
"Koto", 107,
"Kalimba", 108,
"Bagpipe", 109,
"Fiddle", 110,
"Shanai", 111,
"Percussive",
"Tinkle Bell", 112,
"Agogo", 113,
"Steel Drums", 114,
"Woodblock", 115,
"Taiko Drum", 116,
"Melodic Tom", 117,
"Synth Drum", 118,
"Reverse Cymbal", 119,
"Sound Effects",
"Guitar Fret Noise", 120,
"Breath Noise", 121,
"Seashore", 122,
"Bird Tweet", 123,
"Telephone Ring", 124,
"Helicopter", 125,
"Applause", 126,
"Gunshot", 127 } ;
// Data for translating scan codes to octaves and notes
// ----------------------------------------------------
#define NUMSCANS (sizeof key / sizeof key[0])
struct
{
int iOctave ;
int iNote ;
int yPos ;
int xPos ;
char * szKey ;
}
key [] =
{
// Scan Char Oct Note
// ---- ---- --- ----
-1, -1, -1, -1, NULL, // 0 None
-1, -1, -1, -1, NULL, // 1 Esc
-1, -1, 0, 0, "", // 2 1
5, 1, 0, 2, "C#", // 3 2 5 C#
5, 3, 0, 4, "D#", // 4 3 5 D#
-1, -1, 0, 6, "", // 5 4
5, 6, 0, 8, "F#", // 6 5 5 F#
5, 8, 0, 10, "G#", // 7 6 5 G#
5, 10, 0, 12, "A#", // 8 7 5 A#
-1, -1, 0, 14, "", // 9 8
6, 1, 0, 16, "C#", // 10 9 6 C#
6, 3, 0, 18, "D#", // 11 0 6 D#
-1, -1, 0, 20, "", // 12 -
6, 6, 0, 22, "F#", // 13 = 6 F#
-1, -1, -1, -1, NULL, // 14 Back
-1, -1, -1, -1, NULL, // 15 Tab
5, 0, 1, 1, "C", // 16 q 5 C
5, 2, 1, 3, "D", // 17 w 5 D
5, 4, 1, 5, "E", // 18 e 5 E
5, 5, 1, 7, "F", // 19 r 5 F
5, 7, 1, 9, "G", // 20 t 5 G
5, 9, 1, 11, "A", // 21 y 5 A
5, 11, 1, 13, "B", // 22 u 5 B
6, 0, 1, 15, "C", // 23 i 6 C
6, 2, 1, 17, "D", // 24 o 6 D
6, 4, 1, 19, "E", // 25 p 6 E
6, 5, 1, 21, "F", // 26 [ 6 F
6, 7, 1, 23, "G", // 27 ] 6 G
-1, -1, -1, -1, NULL, // 28 Ent
-1, -1, -1, -1, NULL, // 29 Ctrl
3, 8, 2, 2, "G#", // 30 a 3 G#
3, 10, 2, 4, "A#", // 31 s 3 A#
-1, -1, 2, 6, "", // 32 d
4, 1, 2, 8, "C#", // 33 f 4 C#
4, 3, 2, 10, "D#", // 34 g 4 D#
-1, -1, 2, 12, "", // 35 h
4, 6, 2, 14, "F#", // 36 j 4 F#
4, 8, 2, 16, "G#", // 37 k 4 G#
4, 10, 2, 18, "A#", // 38 l 4 A#
-1, -1, 2, 20, "", // 39 ;
5, 1, 2, 22, "C#", // 40 ' 5 C#
-1, -1, -1, -1, NULL, // 41 `
-1, -1, -1, -1, NULL, // 42 Shift
-1, -1, -1, -1, NULL, // 43 \ (not line continuation)
3, 9, 3, 3, "A", // 44 z 3 A
3, 11, 3, 5, "B", // 45 x 3 B
4, 0, 3, 7, "C", // 46 c 4 C
4, 2, 3, 9, "D", // 47 v 4 D
4, 4, 3, 11, "E", // 48 b 4 E
4, 5, 3, 13, "F", // 49 n 4 F
4, 7, 3, 15, "G", // 50 m 4 G
4, 9, 3, 17, "A", // 51 , 4 A
4, 11, 3, 19, "B", // 52 . 4 B
5, 0, 3, 21, "C" // 53 / 5 C
} ;
// Standard WinMain
// ----------------
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
HWND hwnd ;
WNDCLASS wndclass ;
if (!hPrevInstance)
{
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
RegisterClass (&wndclass) ;
}
hwnd = CreateWindow (szAppName, "Keyboard MIDI Player",
WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, nCmdShow) ;
UpdateWindow (hwnd);
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
// Create the program's menu (called from WndProc, WM_CREATE)
// ----------------------------------------------------------
HMENU CreateTheMenu (int iNumDevs)
{
char szBuffer [32] ;
HMENU hMenu, hMenuPopup, hMenuSubPopup ;
int i, iFam, iIns ;
MIDIOUTCAPS moc ;
hMenu = CreateMenu () ;
// Create "On/Off" popup menu
hMenuPopup = CreateMenu () ;
AppendMenu (hMenuPopup, MF_STRING , IDM_OPEN, "&Open") ;
AppendMenu (hMenuPopup, MF_STRING | MF_CHECKED, IDM_CLOSE, "&Closed") ;
AppendMenu (hMenu, MF_STRING | MF_POPUP, hMenuPopup, "&Status") ;
// Create "Device" popup menu
hMenuPopup = CreateMenu () ;
// Put MIDI Mapper on menu if it's installed
if (!midiOutGetDevCaps (MIDIMAPPER, &moc, sizeof (moc)))
AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + MIDIMAPPER,
moc.szPname) ;
else
iDevice = 0 ;
// Add the rest of the MIDI devices
for (i = 0 ; i < iNumDevs ; i++)
{
midiOutGetDevCaps (i, &moc, sizeof (moc)) ;
AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + i, moc.szPname) ;
}
CheckMenuItem (hMenuPopup, 0, MF_BYPOSITION | MF_CHECKED) ;
AppendMenu (hMenu, MF_STRING | MF_POPUP, hMenuPopup, "&Device") ;
// Create "Channel" popup menu
hMenuPopup = CreateMenu () ;
for (i = 0 ; i < 16 ; i++)
{
wsprintf (szBuffer, "%d", i + 1) ;
AppendMenu (hMenuPopup,
MF_STRING | (i ? MF_UNCHECKED : MF_CHECKED),
IDM_CHANNEL + i, szBuffer) ;
}
AppendMenu (hMenu, MF_STRING | MF_POPUP, hMenuPopup, "&Channel") ;
// Create "Voice" popup menu
hMenuPopup = CreateMenu () ;
for (iFam = 0 ; iFam < 16 ; iFam++)
{
hMenuSubPopup = CreateMenu () ;
for (iIns = 0 ; iIns < 8 ; iIns++)
{
wsprintf (szBuffer, "&%d.\t%s", iIns + 1,
(LPSTR) fam[iFam].inst[iIns].szInst) ;
AppendMenu (hMenuSubPopup,
MF_STRING | (fam[iFam].inst[iIns].iVoice ?
MF_UNCHECKED : MF_CHECKED),
fam[iFam].inst[iIns].iVoice + IDM_VOICE,
szBuffer) ;
}
wsprintf (szBuffer, "&%c.\t%s", 'A' + iFam,
(LPSTR) fam[iFam].szFam) ;
AppendMenu (hMenuPopup, MF_STRING | MF_POPUP, hMenuSubPopup,
szBuffer) ;
}
AppendMenu (hMenu, MF_STRING | MF_POPUP, hMenuPopup, "&Voice") ;
return hMenu ;
}
// Routines for simplifying MIDI output
// ------------------------------------
DWORD MidiOutMessage (HMIDIOUT hMidi, int iStatus, int iChannel,
int iData1, int iData2)
{
DWORD dwMessage ;
dwMessage = iStatus | iChannel | (iData1 << 8) | ((long) iData2 << 16) ;
return midiOutShortMsg (hMidi, dwMessage) ;
}
DWORD MidiNoteOff (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel)
{
return MidiOutMessage (hMidi, 0x080, iChannel, 12 * iOct + iNote, iVel) ;
}
DWORD MidiNoteOn (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel)
{
return MidiOutMessage (hMidi, 0x090, iChannel, 12 * iOct + iNote, iVel) ;
}
DWORD MidiSetPatch (HMIDIOUT hMidi, int iChannel, int iVoice)
{
return MidiOutMessage (hMidi, 0x0C0, iChannel, iVoice, 0) ;
}
DWORD MidiPitchBend (HMIDIOUT hMidi, int iChannel, int iBend)
{
return MidiOutMessage (hMidi, 0x0E0, iChannel, iBend & 0x7F, iBend >> 7) ;
}
// Draw a single key on window
// ---------------------------
VOID DrawKey (HDC hdc, int iScanCode, BOOL fInvert)
{
RECT rc ;
rc.left = 3 * cxCaps * key[iScanCode].xPos / 2 + xOffset ;
rc.top = 3 * cyChar * key[iScanCode].yPos / 2 + yOffset ;
rc.right = rc.left + 3 * cxCaps ;
rc.bottom = rc.top + 3 * cyChar / 2 ;
SetTextColor (hdc, fInvert ? 0x00FFFFFFul : 0x00000000ul) ;
SetBkColor (hdc, fInvert ? 0x00000000ul : 0x00FFFFFFul) ;
FillRect (hdc, &rc, GetStockObject (fInvert ? BLACK_BRUSH : WHITE_BRUSH)) ;
DrawText (hdc, key[iScanCode].szKey, -1, &rc,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
FrameRect (hdc, &rc, GetStockObject (BLACK_BRUSH)) ;
}
// Process a Key Up or Key Down message
// ------------------------------------
VOID ProcessKey (HDC hdc, WORD message, LONG lParam)
{
int iScanCode, iOctave, iNote ;
iScanCode = 0x0FF & HIWORD (lParam) ;
if (iScanCode >= NUMSCANS) // No scan codes over 53
return ;
if ((iOctave = key[iScanCode].iOctave) == -1) // Non-music key
return ;
if (GetKeyState (VK_SHIFT) < 0)
iOctave += 0x20000000 & lParam ? 2 : 1 ;
if (GetKeyState (VK_CONTROL) < 0)
iOctave -= 0x20000000 & lParam ? 2 : 1 ;
iNote = key[iScanCode].iNote ;
if (message == WM_KEYUP) // For key up
{
MidiNoteOff (hMidiOut, iChannel, iOctave, iNote, 0) ; // Note off
DrawKey (hdc, iScanCode, FALSE) ;
return ;
}
if (0x40000000 & lParam) // ignore typematics
return ;
MidiNoteOn (hMidiOut, iChannel, iOctave, iNote, iVelocity) ; // Note on
DrawKey (hdc, iScanCode, TRUE) ; // Draw the inverted key
}
// Window Procedure
// ----------------
long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam,
LONG lParam)
{
static BOOL bOpened = FALSE ;
char szBuffer [16] ;
DWORD dwExtent ;
HDC hdc ;
HMENU hMenu ;
MIDIOUTCAPS moc ;
PAINTSTRUCT ps ;
short i, iNumDevs, iPitchBend, cxClient, cyClient ;
switch (message)
{
case WM_CREATE:
// Get size of capital letters in system font
hdc = GetDC (hwnd) ;
dwExtent = GetTextExtent (hdc, "M", 1) ;
cxCaps = LOWORD (dwExtent) ;
cyChar = HIWORD (dwExtent) ;
ReleaseDC (hwnd, hdc) ;
// Initialize "Volume" scroll bar
SetScrollRange (hwnd, SB_HORZ, 1, 127, FALSE) ;
SetScrollPos (hwnd, SB_HORZ, iVelocity, TRUE) ;
// Initialize "Pitch Bend" scroll bar
SetScrollRange (hwnd, SB_VERT, 0, 16383, FALSE) ;
SetScrollPos (hwnd, SB_VERT, 8192, TRUE) ;
// Get number of MIDI output devices and set up menu
if (0 == (iNumDevs = midiOutGetNumDevs ()))
{
MessageBeep (MB_ICONSTOP) ;
MessageBox (hwnd, "No MIDI output devices",
szAppName, MB_OK | MB_ICONSTOP) ;
DestroyWindow (hwnd) ;
}
else
{
SetMenu (hwnd, CreateTheMenu (iNumDevs)) ;
}
return 0 ;
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
xOffset = (cxClient - 25 * 3 * cxCaps / 2) / 2 ;
yOffset = (cyClient - 11 * cyChar) / 2 + 5 * cyChar ;
return 0 ;
case WM_COMMAND:
hMenu = GetMenu (hwnd) ;
// "Open" menu command
if (wParam == IDM_OPEN && !bOpened)
{
if (midiOutOpen (&hMidiOut, iDevice, NULL, 0L, 0L))
{
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (hwnd, "Cannot open MIDI device",
szAppName, MB_OK | MB_ICONEXCLAMATION) ;
}
else
{
CheckMenuItem (hMenu, IDM_OPEN, MF_CHECKED) ;
CheckMenuItem (hMenu, IDM_CLOSE, MF_UNCHECKED) ;
MidiSetPatch (hMidiOut, iChannel, iVoice) ;
bOpened = TRUE ;
}
}
// "Close" menu command
else if (wParam == IDM_CLOSE && bOpened)
{
CheckMenuItem (hMenu, IDM_OPEN, MF_UNCHECKED) ;
CheckMenuItem (hMenu, IDM_CLOSE, MF_CHECKED) ;
// Turn all keys off and close device
for (i = 0 ; i < 16 ; i++)
MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ;
midiOutClose (hMidiOut) ;
bOpened = FALSE ;
}
// Change MIDI "Device" menu command
else if (wParam >= IDM_DEVICE - 1 && wParam < IDM_CHANNEL)
{
CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_UNCHECKED) ;
iDevice = wParam - IDM_DEVICE ;
CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_CHECKED) ;
// Close and reopen MIDI device
if (bOpened)
{
SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ;
SendMessage (hwnd, WM_COMMAND, IDM_OPEN, 0L) ;
}
}
// Change MIDI "Channel" menu command
else if (wParam >= IDM_CHANNEL && wParam < IDM_VOICE)
{
CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_UNCHECKED);
iChannel = wParam - IDM_CHANNEL ;
CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_CHECKED) ;
if (bOpened)
MidiSetPatch (hMidiOut, iChannel, iVoice) ;
}
// Change MIDI "Voice" menu command
else if (wParam >= IDM_VOICE)
{
CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_UNCHECKED) ;
iVoice = wParam - IDM_VOICE ;
CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_CHECKED) ;
if (bOpened)
MidiSetPatch (hMidiOut, iChannel, iVoice) ;
}
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
// Process a Key Up or Key Down message
case WM_KEYUP:
case WM_KEYDOWN:
hdc = GetDC (hwnd) ;
if (bOpened)
ProcessKey (hdc, message, lParam) ;
ReleaseDC (hwnd, hdc) ;
return 0 ;
// For Escape, turn off all notes and repaint
case WM_CHAR:
if (bOpened && wParam == 27)
{
for (i = 0 ; i < 16 ; i++)
MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ;
InvalidateRect (hwnd, NULL, TRUE) ;
}
return 0 ;
// Horizontal scroll: Velocity
case WM_HSCROLL:
switch (wParam)
{
case SB_LINEUP: iVelocity -= 1 ; break ;
case SB_LINEDOWN: iVelocity += 1 ; break ;
case SB_PAGEUP: iVelocity -= 8 ; break ;
case SB_PAGEDOWN: iVelocity += 8 ; break ;
case SB_THUMBPOSITION:
iVelocity = LOWORD (lParam) ;
break ;
default:
return 0 ;
}
iVelocity = max (1, min (iVelocity, 127)) ;
SetScrollPos (hwnd, SB_HORZ, iVelocity, TRUE) ;
return 0 ;
// Vertical scroll: Pitch Bend
case WM_VSCROLL:
switch (wParam)
{
case SB_THUMBTRACK:
iPitchBend = 16383 - LOWORD (lParam) ;
break ;
case SB_THUMBPOSITION:
iPitchBend = 8191 ;
break ;
default:
return 0 ;
}
iPitchBend = max (0, min (iPitchBend, 16383)) ;
SetScrollPos (hwnd, SB_VERT, 16383 - iPitchBend, TRUE) ;
if (bOpened)
MidiPitchBend (hMidiOut, iChannel, iPitchBend) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
for (i = 0 ; i < NUMSCANS ; i++)
if (key[i].xPos != -1)
DrawKey (hdc, i, FALSE) ;
midiOutGetDevCaps (iDevice, &moc, sizeof (MIDIOUTCAPS)) ;
wsprintf (szBuffer, "Channel %d", iChannel + 1) ;
TextOut (hdc, cxCaps, 1 * cyChar, bOpened ? "Open" : "Closed",
bOpened ? 4 : 6) ;
TextOut (hdc, cxCaps, 2 * cyChar, moc.szPname,
strlen (moc.szPname)) ;
TextOut (hdc, cxCaps, 3 * cyChar, (LPSTR) szBuffer,
strlen (szBuffer)) ;
TextOut (hdc, cxCaps, 4 * cyChar,
(LPSTR) fam[iVoice / 8].inst[iVoice % 8].szInst,
strlen (fam[iVoice / 8].inst[iVoice % 8].szInst)) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ;
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}